package com.derbysoft.dswitch.adapter.ihg.common.repository.impl;

import com.derbysoft.dswitch.adapter.ihg.common.domain.TransactionDetail;
import com.derbysoft.dswitch.adapter.ihg.common.domain.TransactionLog;
import com.derbysoft.dswitch.adapter.ihg.common.repository.TransactionLogRepository;
import com.derbysoft.dswitch.adapter.ihg.common.utils.DateTimeUtils;
import com.derbysoft.dswitch.adapter.ihg.common.utils.EnvironmentUtils;
import com.derbysoft.dswitch.adapter.ihg.common.utils.FileUtils;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Date;
import java.util.concurrent.Executor;

@Service("transactionLogRepository")
public class TransactionLogRepositoryImpl implements TransactionLogRepository {

    private static final String CREATE_TRANSACTION_LOG_TABLE_SQL =
            FileUtils.getStringValue(TransactionLogRepositoryImpl.class, "TransactionLog.sql");

    private static final String CREATE_TRANSACTION_LOG_DETAIL_TABLE_SQL =
            FileUtils.getStringValue(TransactionLogRepositoryImpl.class, "TransactionLogDetail.sql");

    @Resource
    protected JdbcTemplate jdbcTemplate;

    @Autowired
    private Executor executor;

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void save(final TransactionLog transactionLog) {
        final String sysDate = DateTimeUtils.formatDate(transactionLog.getTimestamp(), "yyyyMMdd");
        if (requiredFragment()) {
            final String insertLogSQL = createFragmentInsertSQL(sysDate);
            final String insertDetailSQL = createDetailLog(sysDate);
            executor.execute(new Runnable() {
                @Override
                public void run() {

                    createTable(sysDate);

                    jdbcTemplate.update(insertLogSQL, new PreparedStatementSetter() {
                        @Override
                        public void setValues(PreparedStatement ps) throws SQLException {
                            ps.setString(1, transactionLog.getId());
                            ps.setString(2, transactionLog.getTaskId());
                            ps.setString(3, transactionLog.getChannel());
                            ps.setString(4, transactionLog.getHotels());
                            ps.setString(5, transactionLog.getOperationType().name());
                            ps.setTimestamp(6, new Timestamp(transactionLog.getTimestamp().getTime()));
                            ps.setBoolean(7, transactionLog.isError());
                            ps.setLong(8, transactionLog.getTimeSpan());
                            ps.setString(9, transactionLog.getException());
                        }
                    });
                    if (CollectionUtils.isNotEmpty(transactionLog.getTransactionDetails())) {
                        for (final TransactionDetail transactionDetail : transactionLog.getTransactionDetails()) {
                            jdbcTemplate.update(insertDetailSQL, new PreparedStatementSetter() {
                                @Override
                                public void setValues(PreparedStatement ps) throws SQLException {
                                    ps.setString(1, transactionDetail.getDetail());
                                    ps.setString(2, transactionDetail.getDirection().name());
                                    ps.setTimestamp(3, new Timestamp(new Date().getTime()));
                                    ps.setString(4, transactionLog.getId());
                                }
                            });
                        }
                    }
                }
            });
        }
    }


    private String createFragmentInsertSQL(String sysDate) {
        return String.format("INSERT INTO %s (id,task_id,channel,hotels,operation_type,timestamp,error,time_span,exception) VALUES(?,?,?,?,?,?,?,?,?);",
                TRANSACTION_TABLE + UNDER_LINE + sysDate);
    }

    private String createTransactionTable(String date) {
        return CREATE_TRANSACTION_LOG_TABLE_SQL.replace(TRANSACTION_TABLE, TRANSACTION_TABLE + UNDER_LINE + date);
    }

    private String createTransactionDetailTable(String date) {
        return CREATE_TRANSACTION_LOG_DETAIL_TABLE_SQL.replace(TRANSACTION_TABLE, TRANSACTION_TABLE + UNDER_LINE + date)
                .replace(TRANSACTION_DETAIL_TABLE, TRANSACTION_DETAIL_TABLE + UNDER_LINE + date);
    }

    private boolean requiredFragment() {
        return Boolean.valueOf(EnvironmentUtils.get("avail.log.fragment.required"));
    }

    private String createDetailLog(String sysDate) {
        return String.format("INSERT INTO %s(detail,direction,timestamp,%s)  VALUES(?,?,?,?);",
                TRANSACTION_DETAIL_TABLE + UNDER_LINE + sysDate,
                TRANSACTION_TABLE + UNDER_LINE + sysDate);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void setTimeSpan(boolean isInsertFragment, final String id, final long timeSpan) {
        String sysDate = DateTimeUtils.formatDate(DateTimeUtils.today(), "yyyyMMdd");
        String table = isInsertFragment ? TRANSACTION_TABLE + UNDER_LINE + sysDate : TRANSACTION_TABLE;
        final String sql = String.format("UPDATE %s SET time_span = ? WHERE id = ?;", table);
        executor.execute(new Runnable() {
            @Override
            public void run() {
                jdbcTemplate.update(sql, new PreparedStatementSetter() {
                    @Override
                    public void setValues(PreparedStatement ps) throws SQLException {
                        ps.setLong(1, timeSpan);
                        ps.setString(2, id);
                    }
                });
            }
        });
    }

    @Override
    public void createTable(String date) {
        jdbcTemplate.update(createTransactionTable(date));

        jdbcTemplate.update(createTransactionDetailTable(date));
    }
}
